package cn.iocoder.yudao.module.shop.category.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
import cn.iocoder.yudao.module.shop.category.dao.ShopCategoryMapper;
import cn.iocoder.yudao.module.shop.category.pojo.bo.ShopCategoryCodeBO;
import cn.iocoder.yudao.module.shop.category.pojo.po.ShopCategory;
import cn.iocoder.yudao.module.shop.category.pojo.vo.ShopCategoryDepositReqVO;
import cn.iocoder.yudao.module.shop.category.pojo.vo.ShopCategoryListReqVO;
import cn.iocoder.yudao.module.shop.category.pojo.vo.ShopCategoryPageReqVO;
import cn.iocoder.yudao.module.shop.category.pojo.vo.ShopCategorySaveReqVO;
import cn.iocoder.yudao.module.shop.info.dal.dataobject.ShopInfoDO;
import cn.iocoder.yudao.module.shop.info.dal.mysql.ShopInfoMapper;
import jakarta.annotation.Nullable;
import jakarta.annotation.Resource;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import java.util.*;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.module.api.shop.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.shop.category.pojo.po.ShopCategory.*;

/**
 * 行业类目 Service 实现类
 *
 * @author 山野羡民（1032694760@qq.com）
 */
@Service
@Validated
public class ShopCategoryServiceImpl implements ShopCategoryService {
    private static final String SHOP_CATEGORY_ALL_LIST = "xianmin:shop:category_all";
    private static final String SHOP_CATEGORY_DESCENDENT_ID_LIST = "xianmin:shop:category_descendent_ids";

    @Resource
    private ShopCategoryMapper shopCategoryMapper;
    @Resource
    private ShopInfoMapper shopInfoMapper;

    @Override
    public List<ShopCategoryCodeBO> getAllCode() {
        return shopCategoryMapper.selectAllCode();
    }

    @CacheEvict(cacheNames = {SHOP_CATEGORY_ALL_LIST, SHOP_CATEGORY_DESCENDENT_ID_LIST},
            allEntries = true) // allEntries 清空所有缓存，因为操作一个类，涉及到多个子类缓存
    @Override
    public Long createCategory(ShopCategorySaveReqVO createReqVO) {
        // 校验父类目存在
        validateParentShopCategory(createReqVO.getParentId());
        // 校验编码是否唯一
        validateCategoryCodeUnique(createReqVO.getCode(), null);

        // 插入
        ShopCategory category = BeanUtils.toBean(createReqVO, ShopCategory.class);
        shopCategoryMapper.insert(category);
        ShopCategory updateObj = new ShopCategory();
        updateObj.setId(category.getId());
        updateObj.setDepth(getCategoryDepth(category.getId()));
        shopCategoryMapper.updateById(updateObj);
        // 返回
        return category.getId();
    }

    @CacheEvict(cacheNames = {SHOP_CATEGORY_ALL_LIST, SHOP_CATEGORY_DESCENDENT_ID_LIST},
            allEntries = true) // allEntries 清空所有缓存，因为操作一个类，涉及到多个子类缓存
    @Override
    public void updateCategory(ShopCategorySaveReqVO updateReqVO) {
        // 父类目不能是自己
        if (Objects.equals(updateReqVO.getParentId(), updateReqVO.getId())) {
            throw exception(CATEGORY_PARENT_CAN_NOT_SELF);
        }
        // 校验类目是否存在
        ShopCategory category = validateShopCategoryExists(updateReqVO.getId());
        // 校验父类目存在
        validateParentShopCategory(updateReqVO.getParentId());
        // 校验编码是否唯一
        validateCategoryCodeUnique(updateReqVO.getCode(), category.getId());

        // 更新
        ShopCategory updateObj = BeanUtils.toBean(updateReqVO, ShopCategory.class);
        updateObj.setId(category.getId());
        updateObj.setDepth(getCategoryDepth(category.getId()));
        shopCategoryMapper.updateById(updateObj);
    }

    @Override
    public void updateDeposit(ShopCategoryDepositReqVO updateReqVO) {
        // 校验类目是否存在
        ShopCategory category = validateShopCategoryExists(updateReqVO.getId());
        // 校验类目是否一级
        if (Objects.equals(category.getId(), PARENT_ID_ROOT)) {
            throw exception(CATEGORY_DEPOSIT_MUST_FIRST_LEVEL);
        }
        // 更新
        ShopCategory updateObj = BeanUtils.toBean(updateReqVO, ShopCategory.class);
        shopCategoryMapper.updateById(updateObj);
    }

    @CacheEvict(cacheNames = {SHOP_CATEGORY_ALL_LIST, SHOP_CATEGORY_DESCENDENT_ID_LIST},
            allEntries = true) // allEntries 清空所有缓存，因为操作一个类，涉及到多个子类缓存
    @Override
    public void deleteCategory(Long id) {
        // 校验类目是否存在
        ShopCategory category = validateShopCategoryExists(id);
        if (Objects.equals(category.getParentId(), PARENT_ID_ROOT)) {
            throw exception(CATEGORY_FIRST_LEVEL_DELETE_FORBIDDEN);
        }
        // 校验是否还有子类目
        if (shopCategoryMapper.selectCountByParentId(id) > 0) {
            throw exception(CATEGORY_EXISTS_CHILDREN);
        }
        // 校验类目是否绑定了店铺
        Long shopCount = getShopCountByCategoryId(id);
        if (shopCount > 0) {
            throw exception(CATEGORY_HAVE_BIND_SHOP);
        }
        // 删除
        shopCategoryMapper.deleteById(id);
    }

    private void validateCategoryCodeUnique(String code, Long id) {
        if (StrUtil.isEmpty(code)) {
            // 未指定编码则不校验
            return;
        }
        ShopCategory categoryDO = shopCategoryMapper.selectByCode(code);
        if (categoryDO == null) {
            return;
        }
        // 如果 id 为空，说明不用比较是否为相同 id 的快递公司
        if (id == null) {
            throw exception(CATEGORY_CODE_DUPLICATE);
        }
        if (!Objects.equals(categoryDO.getId(), id)) {
            throw exception(CATEGORY_CODE_DUPLICATE);
        }
    }

    private void validateParentShopCategory(Long parentId) {
        if (parentId == null || parentId < PARENT_ID_ROOT) {
            parentId = PARENT_ID_ROOT;
        }
        // 父分类不能是根分类，根分类是预先设定好的主营类目，只能添加在主营类目下
        if (Objects.equals(parentId, PARENT_ID_ROOT)) {
            throw exception(CATEGORY_PARENT_CAN_NOT_FIRST_LEVEL);
        }
        // 父类目不存在
        ShopCategory category = shopCategoryMapper.selectById(parentId);
        if (category == null) {
            throw exception(CATEGORY_PARENT_NOT_EXISTS);
        }
    }

    private ShopCategory validateShopCategoryExists(Long id) {
        ShopCategory category = shopCategoryMapper.selectById(id);
        if (category == null) {
            throw exception(CATEGORY_NOT_EXISTS);
        }
        return category;
    }

    @Override
    public void validateCategoryList(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return;
        }
        // 获得行业类目信息
        List<ShopCategory> list = shopCategoryMapper.selectByIds(ids);
        Map<Long, ShopCategory> categoryMap = CollectionUtils.convertMap(list, ShopCategory::getId);
        // 校验
        ids.forEach(id -> {
            // 校验类目是否存在
            ShopCategory category = categoryMap.get(id);
            if (category == null) {
                throw exception(CATEGORY_NOT_EXISTS);
            }
            // 校验类目是否启用
            if (!CommonStatusEnum.ENABLE.getStatus().equals(category.getStatus())) {
                throw exception(CATEGORY_DISABLED, category.getName());
            }
            // 行业类目层级校验，必须使用第二级的行业类目
            if (getCategoryDepth(id) < CATEGORY_LEVEL_SECOND) {
                throw exception(CATEGORY_LEVEL_ERROR_FOR_SHOP_SAVE);
            }
        });
    }

    @Override
    public PageResult<ShopCategory> getCategoryPage(ShopCategoryPageReqVO reqVO) {
        return shopCategoryMapper.selectPage(reqVO);
    }

    @Override
    public ShopCategory getCategory(Long id) {
        return shopCategoryMapper.selectById(id);
    }

    @Override
    public ShopCategory validateCategory(Long id) {
        ShopCategory category = shopCategoryMapper.selectById(id);
        if (category == null) {
            throw exception(CATEGORY_NOT_EXISTS);
        }
        if (Objects.equals(category.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {
            throw exception(CATEGORY_DISABLED, category.getName());
        }
        return category;
    }

    @Override
    public Integer getCategoryDepth(Long id) {
        if (Objects.equals(id, PARENT_ID_ROOT)) {
            return CATEGORY_LEVEL_ROOT;
        }
        int depth = CATEGORY_LEVEL_FIRST;
        // for 的原因，是因为避免脏数据，导致可能的死循环。一般不会超过 100 层哈
        for (int i = 0; i < Byte.MAX_VALUE; i++) {
            // 如果没有父节点，break 结束
            ShopCategory category = shopCategoryMapper.selectById(id);
            if (category == null || Objects.equals(category.getParentId(), PARENT_ID_ROOT)) {
                break;
            }
            // 继续递归父节点
            depth++;
            id = category.getParentId();
        }
        return depth;
    }

    @DataPermission(enable = false) // 禁用数据权限，避免建立不正确的缓存
    @Cacheable(cacheNames = SHOP_CATEGORY_ALL_LIST)
    @Override
    public List<ShopCategory> getAllCategoryList() {
        return shopCategoryMapper.selectAll();
    }

    @Override
    public List<ShopCategory> getCategoryList(ShopCategoryListReqVO listReqVO) {
        return shopCategoryMapper.selectList(listReqVO);
    }

    @Override
    public List<ShopCategory> getCategoryList(Set<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return new ArrayList<>();
        }
        return shopCategoryMapper.selectByIds(ids);
    }

    @Override
    public List<ShopCategory> getEnableCategoryList(@Nullable Long parentId) {
        return shopCategoryMapper.selectListByStatus(parentId, CommonStatusEnum.ENABLE.getStatus());
    }

    @Override
    public List<ShopCategory> getEnableCategoryList(Collection<Long> ids) {
        return shopCategoryMapper.selectListByIdsAndStatus(ids, CommonStatusEnum.ENABLE.getStatus());
    }

    @Override
    public List<ShopCategory> getDescendentCategoryList(Collection<Long> parentIds, Integer status) {
        List<ShopCategory> children = new LinkedList<>();
        if (CollUtil.isEmpty(parentIds)) {
            return shopCategoryMapper.selectListByStatus(null, status);
        }
        List<ShopCategory> allCategory = SpringUtil.getBean(ShopCategoryService.class).getAllCategoryList();
        if (CollUtil.isEmpty(allCategory)) {
            return children;
        }
        for (int i = 0; i < Short.MAX_VALUE; i++) { // 使用 Short.MAX_VALUE 避免 bug 场景下，存在死循环
            // 查询当前层，所有的子类目
            List<ShopCategory> categories = new ArrayList<>();
            for (ShopCategory category : allCategory) {
                if (parentIds.contains(category.getParentId()) && Objects.equals(category.getStatus(), status)) {
                    categories.add(category);
                }
            }
            // 1. 如果没有子类目，则结束遍历
            if (CollUtil.isEmpty(categories)) {
                break;
            }
            // 2. 如果有子类目，继续遍历
            children.addAll(categories);
            parentIds = convertSet(categories, ShopCategory::getId);
        }
        return children;
    }

    @Override
    public ShopCategory getCategory(String code, Integer status) {
        return shopCategoryMapper.selectOneByCodeAndStatus(code, status);
    }

    @Override
    @DataPermission(enable = false) // 禁用数据权限，避免建立不正确的缓存
    @Cacheable(cacheNames = SHOP_CATEGORY_DESCENDENT_ID_LIST, key = "#parentId-#status")
    public Set<Long> getDescendentCategoryIdsFromCache(Long parentId, Integer status) {
        List<ShopCategory> descendentCategoryList = getDescendentCategoryList(CollUtil.newHashSet(parentId), status);
        return convertSet(descendentCategoryList, ShopCategory::getId);
    }

    @Override
    public Long getParentCategoryId(Long id) {
        return shopCategoryMapper.selectParentIdById(id);
    }

    @Override
    public List<ShopCategory> getEnableCategoryList(String parentCode) {
        return shopCategoryMapper.selectEnableSonsByParentCode(parentCode);
    }

    @Override
    public Long getShopCountByCategoryId(Long categoryId) {
        return shopInfoMapper.selectCount(ShopInfoDO::getCategoryId, categoryId);
    }

    @Override
    public ShopCategory getBySupplyRelatedId(Long supplyRelatedId) {
        return shopCategoryMapper.selectBySupplyRelatedId(supplyRelatedId);
    }

}
